#include "record_utils.h"
#include <sstream>
#include <iomanip>
#include <libgen.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include "base_file_util.h"
#include "base_log.h"

namespace El {
namespace Record {

PathUtils &PathUtils::GetInstance()
{
    static PathUtils instance;
    return instance;
}

std::string PathUtils::GenerateIndexPath(time_t timestamp)
{
    auto tm = std::localtime(&timestamp);
    std::ostringstream indexPath;
    indexPath << storePath_ << "/" << std::put_time(tm, "%Y") << "/" << std::put_time(tm, "%m") << "/"
              << std::put_time(tm, "%d") << "/" << std::put_time(tm, "%Y%m%d") << ".json";
    return indexPath.str();
}

std::string PathUtils::GenerateThumbnailPath(const std::string &imagePath)
{
    char *pathCopy = strdup(imagePath.c_str());
    char *dir = dirname(pathCopy);
    char *parentDir = dirname(dir);
    char *baseFile = basename(const_cast<char *>(imagePath.c_str()));

    // 检查是否为视频文件（.mp4扩展名）
    std::string baseFileName = baseFile;
    if (baseFileName.length() > 4 && baseFileName.substr(baseFileName.length() - 4) == ".mp4") {
        // 将.mp4替换为.jpg
        baseFileName = baseFileName.substr(0, baseFileName.length() - 4) + ".jpg";
    }

    std::string result = std::string(parentDir) + "/thumbnails/" + baseFileName;
    free(pathCopy);
    return result;
}

std::string PathUtils::GetDateDirPath(const std::string &date)
{
    return storePath_ + "/" + date.substr(0, 4) + "/" + // YYYY
           date.substr(4, 2) + "/" +                    // MM
           date.substr(6, 2);                           // DD
}

std::string PathUtils::GetIndexFilePath(const std::string &date)
{
    return GetDateDirPath(date) + "/" + date + ".json"; // YYYYMMDD.json
}

std::string PathUtils::GetVideoFilePath(const std::string &date, const std::string &filename)
{
    return GetDateDirPath(date) + "/videos/" + filename;
}

std::string PathUtils::GetImageFilePath(const std::string &date, const std::string &filename)
{
    return GetDateDirPath(date) + "/images/" + filename;
}

std::string PathUtils::GetParentPath(const std::string &path)
{
    size_t pos = path.find_last_of('/');
    if (pos != std::string::npos) {
        return path.substr(0, pos);
    }
    return path;
}

std::string PathUtils::GenerateFilename(time_t timestamp,
                                        int32_t ch,
                                        int32_t type,
                                        const std::string &fileType,
                                        const std::string &extension)
{
    auto tm = std::localtime(&timestamp);

    // 构建年/月/日/fileType目录路径
    std::ostringstream dirPath;
    dirPath << storePath_ << "/" << std::put_time(tm, "%Y") << "/" // 年目录
            << std::put_time(tm, "%m") << "/"                      // 月目录
            << std::put_time(tm, "%d") << "/"                      // 日目录
            << fileType;

    // 创建目录结构
    std::string fullPath = dirPath.str();
    if (!El::Base::CreateDirectories(fullPath)) {
        LOG_ERROR("Failed to create directory: {}", fullPath);
        return "";
    }

    // 生成完整的文件路径: YYYYMMDD_HHMMSS_ch_type.extension
    std::ostringstream filename;
    filename << dirPath.str() << "/" << std::put_time(tm, "%Y%m%d_%H%M%S") << "_" << std::setfill('0') << std::setw(2)
             << ch << "_" << std::setw(2) << type << extension;

    return filename.str();
}

void PathUtils::TraverseStorageDirectory(const std::function<void(const std::string &, const std::string &)> &callback)
{
    // 确保 storePath_ 是绝对路径
    char resolvedPath[PATH_MAX];
    if (realpath(storePath_.c_str(), resolvedPath) == nullptr) {
        LOG_ERROR("Failed to resolve absolute path for: {}, error: {}", storePath_, strerror(errno));
        return;
    }
    std::string absoluteStorePath(resolvedPath);

    DIR *yearDir = opendir(absoluteStorePath.c_str());
    if (yearDir == nullptr) {
        LOG_ERROR("Failed to open storage directory: {}, error: {}", absoluteStorePath, strerror(errno));
        return;
    }

    struct dirent *yearEntry;
    while ((yearEntry = readdir(yearDir)) != nullptr) {
        if (yearEntry->d_type != DT_DIR || strcmp(yearEntry->d_name, ".") == 0 ||
            strcmp(yearEntry->d_name, "..") == 0) {
            continue;
        }

        std::string yearPath = absoluteStorePath + "/" + yearEntry->d_name;
        DIR *monthDir = opendir(yearPath.c_str());
        if (monthDir == nullptr) {
            LOG_ERROR("Failed to open year directory: {}, error: {}", yearPath, strerror(errno));
            continue;
        }

        struct dirent *monthEntry;
        while ((monthEntry = readdir(monthDir)) != nullptr) {
            if (monthEntry->d_type != DT_DIR || strcmp(monthEntry->d_name, ".") == 0 ||
                strcmp(monthEntry->d_name, "..") == 0) {
                continue;
            }

            std::string monthPath = yearPath + "/" + monthEntry->d_name;
            DIR *dayDir = opendir(monthPath.c_str());
            if (dayDir == nullptr) {
                LOG_ERROR("Failed to open month directory: {}, error: {}", monthPath, strerror(errno));
                continue;
            }

            struct dirent *dayEntry;
            while ((dayEntry = readdir(dayDir)) != nullptr) {
                if (dayEntry->d_type != DT_DIR || strcmp(dayEntry->d_name, ".") == 0 ||
                    strcmp(dayEntry->d_name, "..") == 0) {
                    continue;
                }

                std::string date = std::string(yearEntry->d_name) + monthEntry->d_name + dayEntry->d_name;
                std::string dayPath = monthPath + "/" + dayEntry->d_name;
                callback(date, dayPath);
            }
            closedir(dayDir);
        }
        closedir(monthDir);
    }
    closedir(yearDir);
}

void PathUtils::RemoveDirectoryRecursively(const std::string &path)
{
    DIR *dir = opendir(path.c_str());
    if (dir) {
        struct dirent *entry;
        while ((entry = readdir(dir)) != nullptr) {
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
                continue;
            }
            std::string fullPath = path + "/" + entry->d_name;
            struct stat st;
            if (stat(fullPath.c_str(), &st) == 0) {
                if (S_ISDIR(st.st_mode)) {
                    RemoveDirectoryRecursively(fullPath);
                } else {
                    unlink(fullPath.c_str());
                }
            }
        }
        closedir(dir);
        if (rmdir(path.c_str()) != 0) {
            LOG_ERROR("Failed to remove directory: {} error: {}", path, strerror(errno));
        }
    }
}

bool PathUtils::IsDirectoryEmpty(const std::string &path)
{
    DIR *dir = opendir(path.c_str());
    if (dir) {
        struct dirent *entry;
        while ((entry = readdir(dir)) != nullptr) {
            if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                closedir(dir);
                return false;
            }
        }
        closedir(dir);
        return true;
    }
    return false;
}

bool PathUtils::CleanEmptyStorageDirectory()
{
    try {
        struct stat st;
        if (stat(storePath_.c_str(), &st) != 0) {
            return false;
        }

        bool hasFiles = false;
        std::function<void(const std::string &)> checkDir = [&](const std::string &path) {
            DIR *dir = opendir(path.c_str());
            if (!dir) {
                return;
            }
            struct dirent *entry;
            while ((entry = readdir(dir)) != nullptr) {
                if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
                    continue;
                }
                std::string fullPath = path + "/" + entry->d_name;
                struct stat entryStat;
                if (stat(fullPath.c_str(), &entryStat) == 0) {
                    if (S_ISREG(entryStat.st_mode)) {
                        hasFiles = true;
                        break;
                    } else if (S_ISDIR(entryStat.st_mode)) {
                        checkDir(fullPath);
                        if (hasFiles) {
                            break;
                        }
                    }
                }
            }
            closedir(dir);
        };

        checkDir(storePath_);

        if (!hasFiles) {
            RemoveDirectoryRecursively(storePath_);
            LOG_INFO("Deleted empty storage directory: {}", storePath_);
            return true;
        }
    } catch (const std::exception &e) {
        LOG_ERROR("Error checking/deleting directory {}: {}", storePath_, e.what());
    }
    return false;
}

nlohmann::json PathUtils::LoadIndex(const std::string &indexPath)
{
    nlohmann::json indexJson;
    std::vector<uint8_t> content;

    // 尝试读取索引文件（包含备份恢复机制）
    if (Base::LoadFileWithBackup(indexPath, content)) {
        try {
            if (!content.empty()) {
                indexJson = nlohmann::json::parse(content.begin(), content.end());
                return indexJson;
            }
        } catch (const std::exception &e) {
            LOG_ERROR("Failed to parse index file: {}. Error: {}", indexPath, e.what());
        }
    }

    return nlohmann::json(); // 返回空JSON表示加载失败
}

bool PathUtils::WriteIndex(const std::string &indexPath, const nlohmann::json &indexJson)
{
    // 将JSON转换为字符串，然后转换为二进制数据
    std::string jsonStr = indexJson.dump(4); // 使用4空格缩进，便于阅读
    std::vector<uint8_t> content(jsonStr.begin(), jsonStr.end());

    // 使用备份机制保存文件
    return Base::SaveFileWithBackup(indexPath, content);
}

nlohmann::json PathUtils::ParseMediaFilename(const std::string &filename, const std::string &expectedExtension)
{
    nlohmann::json info;

    // 检查文件名长度和扩展名
    if (filename.length() < 24 || filename.substr(filename.length() - 4) != expectedExtension) {
        LOG_ERROR("Invalid filename format: {}", filename);
        return info;
    }

    try {
        // 解析日期和时间 (YYYYMMDD_HHMMSS)
        std::string dateTimeStr = filename.substr(0, 15);
        std::tm tm = {};
        std::istringstream ss(dateTimeStr);
        ss >> std::get_time(&tm, "%Y%m%d_%H%M%S");
        if (ss.fail()) {
            LOG_ERROR("Failed to parse datetime from filename: {}", filename);
            return info;
        }

        // 解析通道号和类型
        size_t lastUnderscorePos = filename.find_last_of('_');
        size_t secondLastUnderscorePos = filename.find_last_of('_', lastUnderscorePos - 1);
        if (lastUnderscorePos == std::string::npos || secondLastUnderscorePos == std::string::npos) {
            LOG_ERROR("Failed to parse channel and type from filename: {}", filename);
            return info;
        }

        std::string chStr = filename.substr(secondLastUnderscorePos + 1, 2);
        std::string typeStr = filename.substr(lastUnderscorePos + 1, 2);

        // 转换为时间戳
        time_t timestamp = std::mktime(&tm);
        if (timestamp == -1) {
            LOG_ERROR("Failed to convert datetime to timestamp: {}", filename);
            return info;
        }

        // 填充信息
        info["filename"] = filename;
        info["timestamp"] = static_cast<uint32_t>(timestamp);
        info["channel"] = std::stoi(chStr);
        info["type"] = std::stoi(typeStr);

    } catch (const std::exception &e) {
        LOG_ERROR("Exception while parsing filename {}: {}", filename, e.what());
        return nlohmann::json();
    }

    return info;
}

nlohmann::json PathUtils::ParseVideoFilename(const std::string &filename)
{
    auto info = ParseMediaFilename(filename, ".mp4");
    if (info.empty()) {
        return info;
    }
    info["stream_type"] = info["type"];
    info.erase("type");
    info["start_time"] = info["timestamp"];
    info.erase("timestamp");
    return info;
}

nlohmann::json PathUtils::ParseImageFilename(const std::string &filename)
{
    return ParseMediaFilename(filename, ".jpg");
}

} // namespace Record
} // namespace El
